Hacker’s Guide to Visual FoxPro
An irreverent look at how Visual FoxPro really works. Tells you the inside scoop on every command, function, property, event and method of Visual FoxPro.
We live less and less, and we learn more and more. Sensibility is surrendering to intelligence.
—Rémy De Gourmont, Le Chemin de Velours
Visual FoxPro developers have long had VB-envy, at least
with regard to one particular feature: IntelliSense. Type a statement such as
Dim SomeVariable As
in Visual Basic and a list immediately pops up
of all of the types a variable can be defined as—data types as well as objects.
Type “SomeObject.” and you’re presented with a list of the properties,
events and methods for that object. This feature is a great productivity
booster for several reasons: You can quickly pick the item you want from the
list, it avoids spelling mistakes, and it cuts down on trips to the Help file
to figure out the syntax for a little-used command.
VFP 7 cures our inferiority complex by adding IntelliSense to our beloved tool, and makes up for the long wait by providing a better version of IntelliSense than Microsoft’s other products have (in fact, the VFP team has been showing their cool version to the other product teams). Once you start appreciating how much more productive IntelliSense makes you, you’ll wonder how you ever coded without it.
IntelliSense provides VFP developers with several very useful behaviors: automatic keyword completion, command and function syntax tips, and lists of members, values, and most recently used files.
Xbase descendents have always supported two ways to enter
commands and functions: using the full, official keyword (such as BROWSE
) or
using the first four or more characters (such as BROW
). However, veteran
developers will tell you that while it’s fine to use REPL
in the
Command Window, you really should use the full REPLACE
in code for
clarity.
IntelliSense gives us the best of both worlds: You can now
type just enough of a keyword to make it distinct, and then press the spacebar
or Enter in the case of a command or “(“ in the case of a function,
and IntelliSense completes the keyword for you. Type “modi” and press
the spacebar, and IntelliSense replaces it with MODIFY
. Because
some keywords start with the same set of characters, you have to type enough to
distinguish the keyword from similar ones. For example, for MESSAGEBOX()
, you
can’t just type “mess(“; that gets expanded to “MESSAGE(“.
Some commands, such as ALTER TABLE
, REPORT FORM
, and OPEN
DATABASE
, consist of more than one keyword. Since the first keyword must always
be followed by the second, VFP automatically adds that keyword as well. Until
we got used to it, we found ourselves typing “OPEN DATABASE DATABASE
MyData
” because we didn’t notice that VFP automatically inserted
“DATABASE” as soon as we pressed the spacebar after typing
“open.” Thank goodness, old dogs can learn new tricks.
VFP has a long list of SET commands, some of which you don’t
use very often. IntelliSense helps out when you type “set” and press
the spacebar by displaying a list of each of the keywords that can follow it,
such as “deleted” and “exact.” Some commands include the
word “to”, such as SET CLASSLIB TO
. As you’ll see in “I Love
IntelliSense, Now Let’s Change It” when we discuss the IntelliSense
Manager, you can tell IntelliSense whether it should automatically insert the
“to” or not (but we suggest you let it insert “to” and get
used to it).
By default, VFP uses uppercase when it expands keywords, so
even if you type the entire word “modify”, VFP replaces it with
MODIFY
. Fortunately for those who prefer lowercase (like Doug) or
even “camel” case (such as TableUpdate()
), you can control
the case IntelliSense uses through the IntelliSense Manager (which we’ll
discuss later in the section “I Love IntelliSense, Now Let’s Change
It”).
To prevent IntelliSense from expanding a keyword, you can press Ctrl+Space rather than the spacebar at the end of the keyword. We haven’t come across a need to do that yet. To undo an expansion (which we have done), press Ctrl+Z twice. The first Ctrl+Z removes the replacement and the second restores the original keyword, leaving it selected.
Our favorite IntelliSense feature is Quick Info. This
feature is a tip window showing the complete syntax of the command or function
you’re in the process of typing. What a great productivity booster! How many
times do you find yourself bringing up the VFP Help file (or HackFox.CHM)
because you can’t quite remember the exact syntax for ALTER TABLE
or whether
the string to search is the first or second parameter in AT()
? Figure 5-18
shows the Quick Info tip for the REPLACE command.
The tip window remains visible as you continue to type the rest of a command or enter the parameters of a function, method, or event, disappearing only when you move the cursor out of the scope of the keyword (for example, completing a function with “)”, pressing the the spacebar, moving to another line, or moving the cursor with the arrow keys). You can manually hide the tip window by pressing Esc and manually display it with Ctrl+I or the Edit, Quick Info menu item. It’s especially useful for functions, methods and events because the parameter you’re currently typing appears in bold.
With some functions, the tip window displays information
about the values for a specific parameter. For example, the second parameter
for MESSAGEBOX()
is an additive value for the buttons and an icon for the
dialog. IntelliSense makes it easy to figure out which ones to use by showing
the complete list of valid values for this parameter (see Figure 5-19). Other
functions accept only one of a list of predefined values for some parameters.
For instance, the second parameter in DBGetProp()
specifies the type of data
object (connection, database, field, table, or view); the list of values for
the third parameter, the property, varies with the type of object (for example,
DefaultValue
is available only for fields). For the type parameter,
IntelliSense displays a list of the object types; choose the desired type from
the list and IntelliSense inserts it, complete with quotes, in the command
line. The list of values displayed for the property parameter includes only
those applicable to the selected type; again, you can choose the property from
the list to add it to the command line.
The SYS()
function is treated even more specially. Although
it’s only one keyword, it really consists of a large number of functions, with
the specific function determined by the first parameter. IntelliSense displays
a list for this parameter showing not only the numeric values but also the
meaning for each one. Once you’ve selected the one you want, the tip window
shows the appropriate syntax for the rest of the function call.
We’re not sure what we were thinking when we said earlier that Quick Info was our favorite IntelliSense feature. Our favorite feature is really List Members, because it does more to save us typing than any other. When you enter the name of an object and press “.”, VFP displays a list of all members (properties, methods, events and contained members) of the object. Figure 5-20 shows an example of this feature. As you navigate through the list, a tip window shows the description of each member, once again saving you a trip to the Help file.
To add the selected member’s name to the command line, press Tab (the cursor appears right after the member name), Enter (the cursor moves to the next line), or some non-alphabetical character such as Space, “.”, “=”, or “(“ (the character is added at the end of the member name). Press Home, End, Esc, or move the cursor out of the scope of the object to hide the list. You can manually display the list by pressing Ctrl+J or choosing List Members from the Edit menu.
If you like List Members so far, you’ll love this: It works in object hierarchies, too. Imagine how much you’d have to type to programmatically change the caption of the header in the third column of a grid on page 2 of a page frame in a form:
Thisform.PageFrame1.Page2.Grid.Column2.Header.Caption = "some value"
Oops, the name of the grid is actually grdCustomers, so when you run this code, it’ll bomb. List Members saves you from both making a spelling mistake and having to type all that code; after you type the period following Thisform, you can select PageFrame1 from the member list and press “.” to display the member list for the page frame, choose Page2 and press “.” to display the member list for that page, and so on, until finally you choose Caption for the header and press “=” to close the list.
List Members doesn’t just work with native VFP objects; it
works with any instantiated object, such as an ActiveX control you’ve added to
a form or a COM object you’ve instantiated with CREATEOBJECT()
. It even works
with objects you haven’t instantiated yet but have declared your intention to
do so with the new AS
clause in the LOCAL
and PUBLIC
commands. For example,
suppose you add this to your code:
LOCAL loExcel AS Excel.Application
In the rest of the code, when you type loExcel followed by a
period, IntelliSense sees that the variable has been “typed” as an
Excel.Application class, gets the members for that class by reading its type
library, and displays them in the List Members list. The AS
clause doesn’t instantiate
the object; you still have to do that in code. It just tells IntelliSense how
to treat a variable in the editor.
This feature works for any kind of class VFP can
instantiate, native or COM. After you type the AS
keyword and press the
spacebar, a list of types appears. This list includes VFP data types (such as
Integer and Numeric), base classes (including Form and Custom), registered type
libraries (for example, ADODB and Excel), your own classes, and Web Services.
As with many things in VFP, the list of registered types is defined in a table,
so you can add type libraries, classes, Web Services, and other types by using
the IntelliSense Manager, discussed later.
Some object properties accept only a small range of values.
Properties with logical values, such as Form.AutoCenter
, can only be set to .T.
or .F. Form.BorderStyle
is numeric, but can accept only one of a selected list
of values (0 to 3). IntelliSense makes it easy to select the correct value for properties
like these. When you type “=” after certain property names,
IntelliSense displays a list of the valid values for those properties (and
their meanings, in the case of numeric values).
For properties with more complex values, IntelliSense
displays an appropriate dialog, such as a color picker for properties
representing colors (such as BackColor
, FillColor
, and ForeColor
) and an Open
Picture dialog for properties containing image filenames (such as Icon
and
Picture
, although strangely enough, not for DragIcon
, MouseIcon
and
OLEDragPicture
).
As with the List Members feature, List Values supports COM objects as well as native ones. IntelliSense displays a list of values for those properties with enumerations defined in the type library, such an ADO Recordset object’s CursorLocation and LockType properties.
IntelliSense extends the values list for the REPLACE
, MODIFY
MEMO
and MODIFY GENERAL
commands: If a cursor is open in the current work area,
it displays a list of fields in that cursor. A value tip window shows the data
type, size, caption and comment for the selected field. In addition, if you
type “m.” in the Command Window, IntelliSense displays a list of
declared variables. The value tip for each variable shows its current value.
Unfortunately, these features work only in the Command Window, not in an editor
window. Of course, you can always cut and paste from the Command Window into
your code, as you find yourself really loving how much work IntelliSense can do
for you.
The List Values feature doesn’t have its own menu item or hotkey; the List Members item and hotkey (Ctrl+J) serve the same function.
Some VFP commands open or process files. Examples include
all the MODIFY COMMAND
s (such as MODIFY PROGRAM
), OPEN DATABASE
, and REPORT
FORM
. IntelliSense presents a most recently used (MRU) list for these commands,
making it easy to use a file you previously worked with. Figure 5-21 shows this
feature with the OPEN DATABASE
command. You also get an MRU list of directories
with the CD
command, which is really handy for those of us who hop back and
forth between different projects. The USE
command has an MRU list on steroids:
In addition to tables you’ve opened before, it lists the tables and views in
the current database, along with a value tip window showing the comment for the
selected table or view. The “Auto MRU (Most Recently Used) Files”
topic in the VFP Help file has a complete list of commands with MRU lists.
For reasons that escape us, this feature works only in the
Command Window. This is a shame, because when you’re working on a particular
project, you’re likely to use OPEN DATABASE
or USE
in your code on the same set
of files over and over.
You can specify how many items IntelliSense displays by setting the “Most Recently Used list contains” option in the View page of the Tools, Options dialog.
If you prefer that keywords appear in something other than
uppercase, you’ll be frustrated typing them in your desired case only to see
VFP automatically change them to uppercase when it expands them. Similarly, you
might prefer to type the “to” yourself in a command, such as SET
CLASSLIB TO
rather than inadvertently put in duplicates (SET CLASSLIB TO TO
)
because you didn’t notice that VFP inserted it for you. (In the latter case, we
recommend you get over it. It’s just another habit you need to break.)
Fortunately, IntelliSense is data-driven. Most of its settings are stored in a table, the name and path of which are stored in the _FoxCode system variable. The default is FoxCode.DBF in your VFP “user” folder (something like C:\Documents and Settings\YourName\Application Data\Microsoft\Visual FoxPro).
The IntelliSense Manager, available in the Tools menu,
allows you to configure the behavior of IntelliSense. It has several pages of
settings. You can specify how the List Members, Quick Info, and keyword
expansion features work (such as what case to use when IntelliSense expands
keywords), or even disable IntelliSense altogether (although we can’t imagine
why you’d want to do that) in the General page. The Types page allows you to
define the types listed for the AS clause of a LOCAL
, PUBLIC
, LPARAMETERS
,
PARAMETERS
, FUNCTION
, or PROCEDURE
declaration. By default, this list includes
VFP data types and base classes, but you can add other things, such as your own
classes, registered type libraries (for example, ADODB and Excel), and Web
Services.
The Custom page allows you to define your own shortcuts. VFP
comes with a few custom shortcuts already defined, such as “mc”,
which expands to MODIFY COMMAND
, and “mf”, which expands to MODIFY
FILE
. You can define your own shortcuts by entering the abbreviation to replace
and the text to replace it with. For example, you might add “mp” to
expand to MODIFY PROJECT
, especially if you routinely work in multiple
projects. You can even create more complicated shortcuts by entering code that
should execute when you type the abbreviation. See the next section, “Please
Sir, I Want Some More,” for information on scripting shortcuts.
Clicking the Edit Properties button in the Advanced page
displays a dialog in which you can fine-tune advanced IntelliSense behaviors,
such as whether a second keyword (such as “TO
”) is included when SET
commands are expanded, and whether the capitalization of keywords associated
with a command (such as FROM
and WHERE
in the SQL SELECT
command) matches the
capitalization chosen for the command. Click the Cleanup button in the Advanced
page to display a dialog allowing you to restore the default settings for all
records in the FoxCode table (except the custom records you added), pack the
table, remove MRU file entries that no longer exist, or remove all MRU entries.
The IntelliSense Manager application, FoxCode.APP in the VFP home directory, is more than just a fancy way to configure IntelliSense. It’s also the driving code for a lot of what IntelliSense does. A good portion of the code is in support of scripting. (In case you’re wondering how we knew that, it’s because we’ve actually gone through the code. Yes, the source code for the IntelliSense Manager comes with VFP!)
Scripting allows you to define more complicated shortcuts than simply expanding an abbreviation to some text. For example, “dc”, which is one of the predefined shortcuts, expands to the following when it’s entered in an editor window:
DEFINE CLASS classname AS Session OLEPUBLIC
PROCEDURE Init
ENDPROC
PROCEDURE Destroy
ENDPROC
PROCEDURE Error(nError, cMethod, nLine)
ENDPROC
ENDDEFINE
While that simply looks like a block of text, the interesting part is that “classname” is automatically selected so you can type the desired name of the class. That behavior requires some code. If you look at the record for this shortcut in the FoxCode table (look near the end of the table for a record with Abbrev = “dc”), you’ll see the following code in the Data memo field:
LPARAMETERS oFoxcode
IF oFoxcode.Location #1
RETURN "DC"
ENDIF
oFoxcode.valuetype = "V"
TEXT TO myvar TEXTMERGE NOSHOW
DEFINE CLASS ~classname~ AS Session OLEPUBLIC
PROCEDURE Init
ENDPROC
PROCEDURE Destroy
ENDPROC
PROCEDURE Error(nError, cMethod, nLine)
ENDPROC
ENDDEFINE
ENDTEXT
RETURN myvar
The first line in this code is an LPARAMETERS
statement. All
FoxCode script is passed a FoxCodeScript object. IntelliSense creates this
object, filling its properties with information about the FoxCode record, what
you typed, and the current environment (see the “FoxCode Object
Reference” topic in the VFP Help file for a description of each property
of this object). The “dc” script code checks the value of the
Location property and simply returns “DC” if it isn’t 1. Location
indicates where you were typing when the shortcut was invoked; a value of 1
means the PRG editor. So, if you type this shortcut anywhere but in a PRG,
nothing appears to happen, which makes sense, because that’s the only logical
place for a class definition. The return value, which must be a string, is
actually ignored, so a blank string could have been returned with the same
results.
Next, the code sets the ValueType property to “V”. This property specifies what happens after the script code is done; “V” means IntelliSense replaces the shortcut with the return value of the code (since it wasn’t set earlier, IntelliSense ignored the previous “DC” return value). The code then uses the Text command to place several lines of text (in this case, the class definition code) into a variable and returns that variable. Note the “~” characters surrounding “classname” in the text. A single “~” tells IntelliSense where to place the cursor after replacing the shortcut with the text (without a “~”, it places the cursor at the end of the text), while two of them specify text to highlight. This makes it easy for you to complete the text by entering more information (such as the class name in this case). If you’d rather use something other than “~”, set the CursorLocChar property of the passed object to the desired character.
You can do even more complex things in the script code for a shortcut. One of our favorite shortcuts inserts header comments at the start of a program, including the name of the PRG file and the author’s name, email address, and company. None of this is hard-coded in the script, but is instead read dynamically from the editor window title and the Windows Registry.
To create this shortcut, choose IntelliSense Manager from the Tools menu, select the Custom page, type the abbreviation you want to use (such as “header”) in the Replace text box, and click on the Add button to add the shortcut to the list. Click on the Script button to bring up an edit window for the shortcut’s script code, and enter the following code:
LPARAMETERS toFoxCode
LOCAL lcReturn, lcTable
IF toFoxCode.Location <> 0
toFoxCode.ValueType = 'V'
lcReturn = GetText()
ENDIF
RETURN lcReturn
FUNCTION GetText
LOCAL loRegistry, lcKey, lcCompany, lnResult, ;
lcContact, lcAccount, lcEmail, lcText
loRegistry = NEWOBJECT('Registry', ;
home() + 'FFC\Registry.vcx')
lcKey = IIF('NT' $ OS() or '5.0' $ OS(), ;
'Software\Microsoft\Windows NT\CurrentVersion', ;
'Software\Microsoft\Windows\CurrentVersion')
lnResult = loRegistry.GetRegKey('RegisteredOrganization', ;
@lcCompany, lcKey, -2147483646)
IF lnResult <> 0
lcCompany = ''
ENDIF
lnResult = loRegistry.GetRegKey('RegisteredOwner', ;
@lcContact, lcKey, -2147483646)
IF lnResult <> 0
lcContact = ''
ENDIF
lcKey = 'Software\Microsoft\Internet Account Manager'
lnResult = loRegistry.GetRegKey('Default Mail Account', ;
@lcAccount, lcKey, -2147483647)
IF NOT EMPTY(lcAccount)
lcKey = lcKey + '\Accounts\' + lcAccount
lnResult = loRegistry.GetRegKey('SMTP Email Address', ;
@lcEmail, lcKey, -2147483647)
ENDIF
IF lnResult <> 0 OR EMPTY(lcEmail)
lcEmail = ''
ELSE
lcEmail = ', mailto:' + lcEmail
ENDIF
TEXT TO lcText TEXTMERGE NOSHOW
*===============================================================
* Program: <<WONTOP()>>
* Purpose: ~
* Author: <<lcContact>><<lcEmail>>
* Copyright: (c) <<YEAR(DATE())>> <<lcCompany>>
* Last revision: <<DATE()>>
* Parameters:
* Returns:
* Environment in:
* Environment out:
*===============================================================
ENDTEXT
RETURN lcText
After entering this code, close the edit window and click OK to close the IntelliSense Manager.
Like the “dc” script, this code works only from a
code editor, not the Command Window; it uses textmerge to create the text to
replace the abbreviation in the command line; and, with the “~”
character, it tells IntelliSense to put the cursor in the “Purpose”
comment line after the expansion is complete. It has a couple of interesting
wrinkles, though. First, it reads your name, company name, and email address
from the Registry using the FoxPro Foundation Classes (FFC) Registry class so
it can insert them into the header. Second, it uses WONTOP()
to insert the name
of the file being edited. As you can see, script code can be considerably more
complex than simply outputting some text.
For information on how to create your own scripted shortcuts, see the “Using Scripting in FoxCode.dbf” topic in the VFP Help file.
IntelliSense is a wonderful addition to the VFP development environment. It helps you avoid spelling mistakes, boost your productivity, and cut down on trips to the Help file. IntelliSense alone is worth the upgrade cost to VFP 7. If you’re like us, you’ll find IntelliSense so compelling, you’ll even find yourself firing up VFP 7 when you have to maintain VFP 6 applications!